﻿using Core.Entities;
using Core.Enums;
using Core.Tools;
using Core.Writer;
using System;
using System.Collections;
using System.Collections.Generic;

namespace Core.ReadWriter
{
    [Serializable]
    public class DictionaryReadWriter : BaseReadWriter
    {
        private static readonly Dictionary<Type, Type[]> ValueTypeBuffer;
        private static readonly object ValueTypeBufferLock;

        static DictionaryReadWriter()
        {
            ValueTypeBuffer = new Dictionary<Type, Type[]>();
            ValueTypeBufferLock = new object();
        }

        internal static bool TryGetValueType(Type Type, out Type KeyType, out Type ValueType)
        {
            Type[] Result;

            if (ValueTypeBuffer.TryGetValue(Type, out Result))
            {
                goto ReturnResult;
            }

            lock (ValueTypeBufferLock)
            {
                if (ValueTypeBuffer.TryGetValue(Type, out Result))
                {
                    goto ReturnResult;
                }

                var TempType = GetIDictionaryType(Type);

                if (TempType == null)
                {
                    Result = null;
                }
                else
                {
                    Result = TempType.GetGenericArguments();
                }

                ValueTypeBuffer.Add(Type, Result);

                goto ReturnResult;
            }

            ReturnResult:
            if (Result == null)
            {
                KeyType = null;
                ValueType = null;

                return false;
            }

            KeyType = Result[0];
            ValueType = Result[1];

            return true;
        }

        private static Type GetIDictionaryType(Type Type)
        {
            if (Type.IsInterface && Type.IsGenericType && Type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
            {
                return Type;
            }
            else
            {
                var Interfaces = Type.GetInterfaces();

                foreach (var Item in Interfaces)
                {
                    if (Item.IsGenericType && Item.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                    {
                        return Item;
                    }
                }
            }

            return null;
        }

        private IDictionaryEnumerator Enumerator;
        private int CurrentIndex;

        private readonly Type Type;

        private readonly Type KeyType;
        private readonly Type ValueType;

        internal DictionaryReadWriter(Type Type, object Content, Type KeyType, Type ValueType, RWSigns Sign) : base(Content, Sign)
        {
            this.Type = Type;
            this.KeyType = KeyType;
            this.ValueType = ValueType;

            Reset();
        }

        public DictionaryReadWriter(IDictionary Content, Type Type = null, RWSigns Sign = RWSigns.ChangeType) : base(Content, Sign)
        {
            if (Content == null)
            {
                throw new NullReferenceException("Content");
            }

            if (Type == null)
            {
                Type = Content.GetType();
            }

            this.Type = Type;

            if (!TryGetValueType(Type, out KeyType, out ValueType))
            {
                KeyType = typeof(object);
                ValueType = typeof(object);
            }

            Reset();
        }

        private IDictionary IDictionaryContent
        {
            get
            {
                return (IDictionary)InternalContent;
            }
        }


        public override object this[string Name]
        {
            get
            {
                return AsGetValue(IDictionaryContent[Name]);
            }
            set
            {
                object Key = Name;
                object Value = AsSetValue(value);

                if ((Sign & RWSigns.ChangeType) != 0)
                {
                    if (!KeyType.IsInstanceOfType(Key))
                    {
                        Key = Convert.ChangeType(Key, KeyType);
                    }

                    if (!ValueType.IsInstanceOfType(Value))
                    {
                        Value = Convert.ChangeType(Value, ValueType);
                    }
                }

                IDictionaryContent[Key] = Value;

                CurrentIndex = int.MaxValue;
            }
        }

        public override object this[int Index]
        {
            get
            {
                if (Index >= 0 && Index < Count)
                {
                    if (Index < CurrentIndex)
                    {
                        Enumerator = IDictionaryContent.GetEnumerator();

                        CurrentIndex = -1;
                    }

                    while (CurrentIndex < Index)
                    {
                        Enumerator.MoveNext();

                        ++CurrentIndex;
                    }

                    return AsGetValue(Enumerator.Value);
                }

                throw new IndexOutOfRangeException("Index");
            }
            set
            {
                throw new InvalidOperationException("不能通过索引设置字典元素。");
            }
        }

        public override IEnumerable<string> Fields
        {
            get
            {
                ICollection Keys = IDictionaryContent.Keys;

                if (Keys is IEnumerable<string>)
                {
                    return (IEnumerable<string>)Keys;
                }

                return new ElementEnumerable<string>(Keys, Object =>
                {
                    if (Object == null)
                    {
                        return null;
                    }

                    return Convert.ToString(Object);
                });
            }
        }

        public override int Count
        {
            get
            {
                return IDictionaryContent.Count;
            }
        }

        public override RWTypes ObjectType
        {
            get
            {
                return RWTypes.Object;
            }
            set
            {
                if (value != RWTypes.Object)
                {
                    throw new NotSupportedException("DictionaryReadWriter ObjectType only be array.");
                }
            }
        }

        public override void Add(string Name, object Value)
        {
            object Key = Name;
            Value = AsSetValue(Value);

            if ((Sign & RWSigns.ChangeType) != 0)
            {
                if (!KeyType.IsInstanceOfType(Key))
                {
                    Key = Convert.ChangeType(Key, KeyType);
                }

                if (!ValueType.IsInstanceOfType(Value))
                {
                    Value = Convert.ChangeType(Value, ValueType);
                }
            }

            IDictionaryContent.Add(Key, Value);

            CurrentIndex = int.MaxValue;
        }

        public override IDataWriter CreateChildrenWriter(CreateChildrenWriterInfo Info)
        {
            if (ValueType != typeof(object))
            {
                return Create(ValueType, Sign);
            }

            return base.CreateChildrenWriter(Info);
        }

        public override void Reset()
        {
            Enumerator = null;
            CurrentIndex = int.MaxValue;

            base.Reset();
        }

        public override bool TryGetValue(string Name, out object Value)
        {
            if (InternalContent is IDictionary<string, object>)
            {
                var Result = ((IDictionary<string, object>)InternalContent).TryGetValue(Name, out Value);

                Value = AsGetValue(Value);

                return Result;
            }

            bool ContainKey = IDictionaryContent.Contains(Name);

            if (!ContainKey)
            {
                Value = null;

                return false;
            }

            Value = AsGetValue(IDictionaryContent[Name]);

            return true;
        }

        public override object Content
        {
            get
            {
                if (Type.IsInstanceOfType(InternalContent))
                {
                    return InternalContent;
                }

                if ((Sign & RWSigns.ChangeType) != 0)
                {
                    return Convert.ChangeType(InternalContent, Type);
                }

                return InternalContent;
            }
        }
    }
}